﻿//////////////////////////////////////////////////////////////////////////////////
//
// ActionScript 3.0 Common Library (MIT License)
// Copyright (c) 2009 AS3CommonCode | Tony Birleffi | http://www.mosaiceye.com
// 
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////////

package org.as3commoncode.net.loader 
{
	import flash.system.ApplicationDomain;	
	import flash.system.SecurityDomain;		import flash.system.LoaderContext;	
	import flash.display.DisplayObject;	
	import flash.display.Bitmap;		import flash.errors.IllegalOperationError;	
	import flash.display.LoaderInfo;    
	import flash.display.Loader;
    import flash.events.Event;
    import flash.events.HTTPStatusEvent;
	import flash.events.IEventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.net.URLRequest;
    
	import org.as3commoncode.net.loader.AbstractLoader;	import org.as3commoncode.events.MediaLoaderEvent;	
	/**
	 * Loads a SWF, JPEG, progressive JPEG, unanimated GIF, or PNG file into an object that 
	 * is a child of this Loader object. And extends the AbstractLoader class.
	 * 
	 * @example
	 * <listing>
	 * var ml:MediaLoader = new MediaLoader("media.swf");
	 * ml.addEventListener(MediaLoaderEvent.INIT, onLoadInit);
	 * ml.addEventListener(MediaLoaderEvent.PROGRESS, onLoadProgress);
	 * ml.addEventListener(MediaLoaderEvent.COMPLETE, onLoadComplete);
	 * ml.load();
	 * </listing>
	 * 
	 * @see org.as3commoncode.net.loader.AbstractLoader;
	 * @see org.as3commoncode.events.MediaLoaderEvent;
	 * 
	 * @author Tony Birleffi
	 */
    public class MediaLoader extends AbstractLoader 
	{
		/** Instance of the loader class. */
        private var loader:Loader;
		
		/** Media info object. */
		private var mediaInfo:Object;
		
		/** Percentage of the load. */
		private var percent:Number = 0;
		
		/** The media file's width. */
		private var width:Number = 0;
		
		/** The media file's height. */
		private var height:Number = 0;
		
		/** Whether to do bitmap smoothing on the image or not. */
		private var smooth:Boolean = true;
		
		/** A bitmap representation of the file loaded, if a Bitmap. */
		private var bmp:Bitmap = null;
		
		/** Whether to use the same domain or not. */
		private var tContent:Boolean = false;
		
		/**
         * Handles the initial setup.
         * 
		 * @param url				String, path to the media file.
		 * @param smoothBitmap		Boolean, to be a smooth image or not.
         */
        public function MediaLoader(url:String = null, smoothBitmap:Boolean = false) 
		{
			// Call the super.
            super(new URLRequest(url));
			
			// Set the smoothing.
			this.smooth = smoothBitmap;
			
			// Make an instance of the loader class.
            this.loader = new Loader();
        }

        /**
         * Loads a swf or image file from the specified url, if you specified it 
		 * already in the constructor you can omit it.
		 * 
		 * @param urlRequest		an url request.
         */
        public override function load(urlRequest:URLRequest = null):void 
		{
			// You can also setup the URL request here as well.
            if(urlRequest != null) request = urlRequest;

			// Add the listeners.
			configureListeners(this.loader.contentLoaderInfo);

			// Setup the loader context.
			var lc:LoaderContext = new LoaderContext();
			lc.checkPolicyFile = true;
			
			// For the trust content or not.
			if(tContent) lc.securityDomain = SecurityDomain.currentDomain;
			else lc.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
                
			// Start the load.
            this.loader.load(request, lc);
        }

        /**
         * Stops the download of the file.
         */
        public override function stop():void 
		{
			// If the load is running, close the load.
            if(running)
			{
				try { 
					this.loader.close();
				} catch(e:Error) {
					throw new IllegalOperationError("The loader never closed.");
				}
            }
			
			// Stop the load.
            this.loader.unload();
            running = false;
        }
        
        /**
		 * Should be called when the load is finished.
		 * 
		 * @param event			event object.
		 */
		private function mediaLoaded(event:Event = null):void
		{	
			// Stop the running.
			running = false;
			
			// Checks if the image is a bitmap.
			if(this.smooth)
			{
	            if(this.loader.content is Bitmap)
	            {
	                bmp = this.loader.content as Bitmap;
	                if(bmp != null && bmp.smoothing == false) bmp.smoothing = smoothing;
	            }
			}
            
			// Dispatch.
			sendEvent(MediaLoaderEvent.COMPLETE);
		}
		
		/**
		 * Method used to send through multiple custom events.
		 * 
		 * @param event		string event name.
		 */
		private function sendEvent(event:String):void
		{
			dispatchEvent(new MediaLoaderEvent(event, false, false, fileLoader, info, 
												 percentage, fileWidth, fileHeight));
		}
		
		/**
         * Simply delegates the event.
         * 
		 * @param event			the event object.
         */
        private function delegate(event:Event):void 
		{
            dispatchEvent(event);
        }
       	
       	/**
       	 * Handles the initialization of the load.
       	 * 
       	 * @param event		the event object.
       	 */
       	private function initHandler(event:Event):void 
       	{
       		// Define and grab the loader info object.
            var loader:Loader = Loader(event.target[ 'loader' ]);
            var info:LoaderInfo = LoaderInfo(loader.contentLoaderInfo);
            
            // Grab needed properties.
            this.mediaInfo = info;
            this.width = mediaInfo[ 'width' ];
            this.height = mediaInfo[ 'height' ];
            
            // Dispatch.
			sendEvent(MediaLoaderEvent.INIT);
        }
        
        /**
       	 * Handles the progress of the load.
       	 * 
       	 * @param event		the event object.
       	 */
       	private function progress(event:ProgressEvent):void 
       	{
       		// Run the percentage calc.
            percent = (event.bytesLoaded / event.bytesTotal);
            
            // Dispatch.
			sendEvent(MediaLoaderEvent.PROGRESS);
        }
        
       	/*
         * Simply configures the event listeners.
         * 
		 * @param dispatcher	the event dispatcher interface.
         */
		private function configureListeners(dispatcher:IEventDispatcher):void
		{
			dispatcher.addEventListener(Event.COMPLETE, mediaLoaded);
			dispatcher.addEventListener(Event.INIT, initHandler);
			dispatcher.addEventListener(ProgressEvent.PROGRESS, progress);
			dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, delegate);
			dispatcher.addEventListener(IOErrorEvent.IO_ERROR, delegate);
			dispatcher.addEventListener(Event.OPEN, delegate);
			dispatcher.addEventListener(Event.UNLOAD, delegate);
		}
        
        /**
         * Returns the smoothing boolean.
         * 
         * @return Boolean.
         */
        public function get smoothing():Boolean
        {
        	return this.smooth;
		}
		
		/**
         * Sets the smoothing boolean.
         * 
         * @param value		Boolean.
         */
        public function set smoothing(value:Boolean):void
        {
        	this.smooth = value;
		}
        
        /**
         * Returns the loader instance that has loaded the swf/image from the given url.
         * 
         * @return Loader.
         */
        public function get fileLoader():Loader 
		{
            return this.loader;
        }
        
        /**
         * Returns the loader content.
         * 
         * @return DisplayObject.
         */
        public function get content():DisplayObject 
		{
			if(this.loader.content) return this.loader.content;
			return null;
        }
        
        /**
         * Returns the loader's content if a bitmap.
         * 
         * @return Bitmap.
         */
        public function get bitmap():Bitmap 
		{
            return this.bmp;
		}
        
        /**
         * Returns the loader instance info, ex: (width, height, url).
         * 
         * @return Object.
         */
        public function get info():Object 
		{
            return this.mediaInfo;
        }
        
        /**
         * Returns the loader percent.
         * 
         * @return Number.
         */
        public function get percentage():Number 
		{
            return this.percent;
        }
        
        /**
         * Returns the media file's width.
         * 
         * @return Number.
         */
        public function get fileWidth():Number 
		{
            return this.width;
        }
        
        /**
         * Returns the media file's height.
         * 
         * @return Number.
         */
        public function get fileHeight():Number 
		{
            return this.height;
        }
        
        /**
         * Get the trust content boolean.
         * 
         * @return Boolean.
         */
        public function get trustContent():Boolean
        {
        	return tContent;
        }
        
        /**
         * Set the trust content boolean.
         * 
         * @param value		Boolean.
         */
        public function set trustContent(value:Boolean):void
        {
        	tContent = value;
        }
    }
}